package com.wontube.smr.bo.sites.youtube.impl;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wontube.client.extension.utils.StringUtils;
import com.wontube.client.extension.utils.net.URLUtils;
import com.wontube.core.pub.AppEnvironment;
import com.wontube.core.utils.net.SocketHttp;
import com.wontube.smr.bo.sites.youtube.IYouTubeService;
import com.wontube.smr.conf.LocalIPList;
import com.wontube.smr.model.vo.youtube.YouTubeVideoFile;
import com.wontube.smr.model.vo.youtube.YouTubeVideoType;






/**
 * @author yuechao
 * 
 */
public final class APIYouTubeService implements IYouTubeService {
	private String source;

	private String pageUrl;
	private String videoTitle;
	private String cookie;
	private String errMsg;
	private final List<YouTubeVideoFile> formats = new ArrayList<YouTubeVideoFile>();

	private static final Logger logger = LoggerFactory.getLogger(APIYouTubeService.class);

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#createEmpty()
	 */
	@Override
	public IYouTubeService createEmpty() {
		return new APIYouTubeService();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#obtain(java.lang.String)
	 */
	@Override
	public boolean obtain(String videoUrl) {
		this.pageUrl = videoUrl;
		try {
			this.downloadSource();
			this.parse();
		} catch (Exception ex) {
			this.errMsg = ex.getMessage();
			return false;
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#getPageUrl()
	 */
	@Override
	public String getPageUrl() {
		return this.pageUrl;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#getVideoTitle()
	 */
	@Override
	public String getVideoTitle() {
		return this.videoTitle;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#getCookie()
	 */
	@Override
	public String getCookie() {
		return this.cookie;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#getFormats()
	 */
	@Override
	public YouTubeVideoFile[] getFormats() {
		return this.formats.toArray(new YouTubeVideoFile[0]);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see smr.bo.video.youtube.IYouTubeService#getAvailableAudioFormat()
	 */
	@Override
	public YouTubeVideoFile[] getAvailableAudioFormat() {
		List<YouTubeVideoFile> list = new ArrayList<YouTubeVideoFile>();
		for (YouTubeVideoFile v : this.formats) {
			if (v.getVideoFormat() == YouTubeVideoType.Flash) {
				list.add(v);
			}
		}

		return list.toArray(new YouTubeVideoFile[0]);
	}
	
	@Override
	public String getErrMsg() {
		return this.errMsg;
	}

	private void downloadSource() throws RuntimeException, IOException {
		List<String> ipList = LocalIPList.IPList.getIPList();
		Random random = new Random();
		int index = random.nextInt(ipList.size());
		String localIP = ipList.get(index);

		SocketHttp http = new SocketHttp(localIP);
		http.setTimeout(20000);
		logger.info("get youtube xml source with ip: {}", localIP);
		InputStreamReader isr = null;
		try {
			index = this.pageUrl.indexOf("?");
			if (index < 0) {
				throw new RuntimeException("Invalid YouTube Url");
			}
			String queryString = this.pageUrl.substring(index + 1);
			Map<String, String> map = URLUtils.parseQueryString(queryString);
			String videoId = map.get("v");
			if (StringUtils.isNullOrEmpty(videoId)) {
				throw new RuntimeException("Invalid YouTube Url");
			}

			String apiUrl = "http://www.youtube.com/get_video_info?video_id=" 
					+ videoId
					+ "&el=embedded&ps=default&autoplay=1&eurl=http%3A%2F%2Fwww%2Eyoutube%2Ecom&hl=en_US";
			if (!http.get(apiUrl)) {
				throw new RuntimeException("Server error: " + http.getResponseMessage());
			}
			isr = new InputStreamReader(http.getInputStream(), AppEnvironment.charsetName);

			StringBuilder sb = new StringBuilder();
			char[] buffer = new char[1024];
			while (true) {
				int len = isr.read(buffer, 0, buffer.length);
				if (len <= 0) {
					break;
				}
				sb.append(buffer, 0, len);
			}

			this.source = sb.toString();
			this.cookie = http.getHeaderValue("Set-Cookie");
		} finally {
			if (isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
				}
			}
			http.close();
		}
	}

	private void parse() throws IOException, URISyntaxException {
		String playbackToken = this.source;
		if (playbackToken.startsWith("account_playback_token=")) {
			playbackToken = playbackToken.substring("account_playback_token=".length() + 1);
		}
		Map<String, String> map = URLUtils.parseQueryString(playbackToken);
		if ("fail".equalsIgnoreCase(map.get("status"))) {
			String reason = map.get("reason");
			reason = URLDecoder.decode(reason, AppEnvironment.charsetName);
			throw new RuntimeException(reason);
		}

		String title = map.get("title");
		this.videoTitle = URLDecoder.decode(title, AppEnvironment.charsetName);
		String availableFormats = map.get("url_encoded_fmt_stream_map");

		String argument = "url%3D";
		String endOfQueryString = "%26";

		if (!"".equals(availableFormats)) {
			String[] urlList = availableFormats.split(argument);
			for (String url : urlList) {
				String u = url.trim();
				if (!"".equals(u)) {
					int index = u.indexOf(endOfQueryString);
					u = u.substring(0, index);
					// 参数是二次编码的
					u = URLDecoder.decode(u, AppEnvironment.charsetName);
					u = URLDecoder.decode(u, AppEnvironment.charsetName);
					URL uri = new URL(u);
					Map<String, String> queryString = URLUtils.parseQueryString(uri.getQuery());
					String itag = queryString.get("itag");
					if (itag == null) {
						continue;
					}

					byte formatCode = Byte.parseByte(itag);
					this.formats.add(new YouTubeVideoFile(u, formatCode));
				}
			}
		}
	}
}
